#pragma once
#include <cstdio>
#include <iostream>
#include <stdlib.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <assert.h>
#include <unistd.h>
#include <netdb.h>

using namespace std;

class hsock_ifs_t
{
	public:
	int n;
	//void send(char *s);
	//void recv(char *s);
};

template <class type>
class arg_hsock_t
{
	public:
	type *t;
	int sock;
	char *ip;
	int port;
};

template <class type>
void *func(void *a)
{
	arg_hsock_t<type>  *b = (arg_hsock_t<type>*)a;
	b->t->serve(b->sock, b->ip, b->port);
	return 0;
}

class hsock_t
{
	pthread_t thr[100];
	int nth;
	
	public:
	
	// start server. type must have "void serve(int socket)" method
	template <class  type>
	void server(type *srv, int portno)
	{
		nth = 0;
		int sockfd, newsockfd, n;
		socklen_t clilen;
		char bf[255];
		struct sockaddr_in serv_addr, cli_addr;
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		if (sockfd < 0) 
			cout << "ERROR opening socket" << endl;	
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_port = htons(portno);
		serv_addr.sin_addr.s_addr = INADDR_ANY;
		if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)))
		cout << "ERROR binding. Make sure that only one instanse of programm running." << endl;
		listen(sockfd, 5);
		clilen = sizeof(cli_addr);
		while (1)
		{
			newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
			if (newsockfd < 0) cout << "ERROR on accept";
			else cout << "connected\n";
			// start thread
			arg_hsock_t<type> p;
			p.t = srv;
			p.sock = newsockfd;
			p.ip = new char [255];
			strcpy(p.ip, inet_ntoa(cli_addr.sin_addr));
			p.port = cli_addr.sin_port;

			pthread_create(&thr[nth], NULL, func<type>, &p);
			//pthread_join(thr[nth], NULL);
			nth++;
		}
	}
	// 25 novenber -123GK
	// connect to server
	int client(char *ip, int portno)
	{
		int sockfd, n;
		struct sockaddr_in serv_addr;
		struct sockaddr_in loc_addr;
		char bf[255];
		strcpy(bf,ip);
		for (int i = 0; i<strlen(ip); i++) if (bf[i]=='\n') bf[i]='\0';
		
		sockfd = socket(AF_INET, SOCK_STREAM, 0);
		
		// set local port
		
	//	memset(&loc_addr, 0, sizeof(struct sockaddr_in));
    /*	loc_addr.sin_family = AF_INET;
   	 	loc_addr.sin_port = htons(4444);
    	loc_addr.sin_addr.s_addr = INADDR_ANY;
    	int ret = bind(sockfd, (struct sockaddr *)&loc_addr, sizeof(struct sockaddr));
	*/ 
	  
		// set sever info
		struct hostent *server = gethostbyname(bf);
		
		if (sockfd < 0) 
		cout << "ERROR opening socket" << endl;	
		memcpy(&serv_addr.sin_addr.s_addr, server->h_addr, server->h_length);
		serv_addr.sin_family = AF_INET;
		serv_addr.sin_port = htons(portno);
		if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)))
			cout << "ERROR connecting.";
		return sockfd;
	}
	
};

